package com.frinder.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * Created by kiktech on 2017/8/9.
 */
@ChannelHandler.Sharable
public abstract class ConnectionWatchDog extends ChannelInboundHandlerAdapter implements TimerTask {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final Bootstrap bootstrap;
    private final Timer timer;
    private final int port;
    private final String host;
    private volatile boolean reconnect = true;
    private int attempts;

    public ConnectionWatchDog(Bootstrap bootstrap, Timer timer, int port, String host, boolean reconnect) {
        this.bootstrap = bootstrap;
        this.timer = timer;
        this.port = port;
        this.host = host;
        this.reconnect = reconnect;
    }

    /**
     * 注册所有 handlers
     *
     * @return
     */
    abstract ChannelHandler[] handlers();

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        attempts = 0;
        ctx.fireChannelActive();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.warn("Connection closed!");
        if (reconnect) {
            if (attempts < 12) {
                attempts++;
                int timeout = 2 << attempts;
                logger.warn("Connection closed, it's {} times try to reconnect, timeout {} milli seconds!", attempts, timeout);
                timer.newTimeout(this, timeout, TimeUnit.MILLISECONDS);
            }
        }
        ctx.fireChannelInactive();
    }

    @Override
    public void run(Timeout timeout) throws Exception {
        ChannelFuture future;
        synchronized (bootstrap) {
            bootstrap.handler(new ChannelInitializer<Channel>() {
                @Override
                protected void initChannel(Channel ch) throws Exception {
                    ch.pipeline().addLast(handlers());
                }
            });
            future = bootstrap.connect(host, port);
        }
        //future对象
        future.addListener(
                (ChannelFuture f) -> {
                    boolean succeed = f.isSuccess();
                    if (!succeed) {
                        logger.error("Reconnect fail!");
                        f.channel().pipeline().fireChannelInactive();
                    } else {
                        logger.info("Reconnect successfully!");
                    }
                }
        );
    }

}